package org.moonshiner.controller.web;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.moonshiner.controller.comparator.CommitHistoryComparator;
import org.moonshiner.controller.svn.SVNRepositoryProvider;
import org.moonshiner.model.MySvnLogEntry;
import org.moonshiner.model.constants.ErrorCode;
import org.moonshiner.model.context.AppContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.io.SVNRepository;

@Controller
@RequestMapping(value = "/svn")
public class RepositoryNavigator {

	private String lastModified;

	@RequestMapping(value = "/nav", method = RequestMethod.GET)
	public String navigate(
			@RequestParam(value = "path", required = false) String path,
			HttpSession session, ModelMap model, HttpServletResponse response) {

		String result = "";

		List<String> errors = new ArrayList<String>();

		SVNRepository repository = ((SVNRepositoryProvider) AppContext
				.getAppContext().getBean(SVNRepositoryProvider.BEAN_NAME))
				.getRepository();

		// SVNRepository repository = MockRepository.getMockRepository();

		path = generatePath(path, repository);

		try {
			SVNNodeKind nodeKind = repository.checkPath(path, -1);
			if (nodeKind == SVNNodeKind.FILE) {
				writeFileToOUtoutStream(path, response, repository);
				result = null;
			} else if (nodeKind == SVNNodeKind.DIR) {
				getEntriesByPath(path, model, errors, repository);
				result = "svn/repoBrowser";
			}
			model.addAttribute("logEntries", getLastCommits(3, repository));
		} catch (SVNException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return result;
	}

	@RequestMapping(value = "/commitHistory")
	public @ResponseBody
	List<MySvnLogEntry> getCommitHistory(HttpServletRequest request,
			HttpServletResponse response) throws SVNException {

		SVNRepository repository = ((SVNRepositoryProvider) AppContext
				.getAppContext().getBean(SVNRepositoryProvider.BEAN_NAME))
				.getRepository();
		// SVNRepository repo = MockRepository.getMockRepository();
		List<MySvnLogEntry> logEntries = getLastCommits(3, repository);
		long prevLastRevision;
		try {
			prevLastRevision = Long.parseLong(request.getParameter("revision"));
		} catch (NumberFormatException e) {
			prevLastRevision = 0;
		}
		;

		if (prevLastRevision < logEntries.get(0).getRevision()) {
			prevLastRevision = logEntries.get(0).getRevision();
			lastModified = new Date().toString();
		} else {
			response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
		}
		response.setHeader("Last-Modified", lastModified);
		return logEntries;
	}

	private String generatePath(String path, SVNRepository repository) {

		String pathStart = repository.getLocation().toString();
		String rootPath = "";
		try {
			rootPath = repository.getRepositoryRoot(true).toString();
		} catch (SVNException e) {
			e.printStackTrace();
		}

		int offset = pathStart.compareTo(rootPath);

		pathStart = pathStart.substring(pathStart.length() - offset);

		if (!path.startsWith(pathStart)) {
			if (path.isEmpty()) {
				path = pathStart;
			} else {
				path = pathStart + "/" + path;
			}
		}
		return path;
	}

	private void writeFileToOUtoutStream(String path,
			HttpServletResponse response, SVNRepository repository)
			throws SVNException, IOException {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		SVNProperties properties = new SVNProperties();
		repository.getFile(path, -1, properties, outputStream);
		outputStream.writeTo(response.getOutputStream());
	}

	@SuppressWarnings("unchecked")
	private List<MySvnLogEntry> getLastCommits(long amount,
			SVNRepository repository) throws SVNException {

		long lastRevisionNumber = repository.getLatestRevision();

		List<MySvnLogEntry> mySvnLogEntries = new ArrayList<MySvnLogEntry>();

		List<SVNLogEntry> svnLogEntries = (List<SVNLogEntry>) repository.log(
				new String[] { "" }, null, lastRevisionNumber - amount + 1, -1,
				true, true);

		for (SVNLogEntry svnLogEntry : svnLogEntries) {
			mySvnLogEntries.add(new MySvnLogEntry(svnLogEntry.getDate(),
					svnLogEntry.getAuthor(), svnLogEntry.getMessage(),
					svnLogEntry.getRevision()));
		}

		Collections.sort(mySvnLogEntries, new CommitHistoryComparator());

		return mySvnLogEntries;
	}

	private List<SVNDirEntry> getEntriesByPath(String path, ModelMap model,
			List<String> errors, SVNRepository repository) {

		if (path == null) {
			path = "";
		}

		List<SVNDirEntry> dirEntries = new ArrayList<SVNDirEntry>();
		try {
			repository.getDir(path, -1, null, dirEntries);
		} catch (SVNException e) {
			if (e.getMessage().contains(ErrorCode.PATH_NOT_FOUND)) {
				errors.add("Path not found");
			}

		}
		model.addAttribute("errors", errors);
		model.addAttribute("svnDirEntries", dirEntries);
		model.addAttribute("path", path);
		return dirEntries;
	}

}
